-
Notifications
You must be signed in to change notification settings - Fork 107
feat: Error Details Improvements - GRPC #1634
Conversation
|
||
public CancelledException( | ||
Throwable cause, StatusCode statusCode, boolean retryable, ErrorDetails errorDetails) { | ||
super(cause, statusCode, retryable, errorDetails); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subjective: For this an all similar changes, can you make them such that each of the overloaded constructors call this()
(except the last one, which does the job). This is an idiomatic way of doing it and is often easier to maintain
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean making the first constructor call this(cause, statusCode, retryable, null)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep. It is a typical approach: to have some sort of a "master" consturctor and make other constructors simply call the big one. The master constructor can even be private, if we don't want to expose it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this is ideal if possible, but I think it depends on whether super(field1, field2)
has the same behavior as this(field1, field2, null)
, sometimes they don't. For example, super(cause)
and this(null, cause)
in RuntimeException
have different behaviors, one will populate message from cause, the other will set it to null. That being said, since this is a side refactoring, maybe we can merge the main logic of the code first and come back to see if we can improve it.
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcApiExceptionFactory.java
Outdated
Show resolved
Hide resolved
} catch (InvalidProtocolBufferException e) { | ||
// If unpacking one of the error detail fails, it should not block unpacking other error | ||
// details so that end users can still get some info back. Hence, we don't need to do | ||
// anything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we just swallow this, if details are all corrupt we keep continuing and then create an empty error details and have no insight into what's going on?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, in this case, an empty ErrorDetails will be created. I think an empty ErrorDetails is a legitimate case, it could happen either when server doesn't send anything or we are unable to unpack the message server sends.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we in his case just store the raw Any (not unpacked) detail
object in ErrorDetails
? Doing empyt error details will just loose information for users and make the error even more confusing to them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Storing raw Any would not be useful to users in my opinion, they don't know how to unpack it and even if they do they can not unpack it either due to exceptions. It might be more confusing to them because they don't know what it is and don't know what to do with it.
What I can do though is to add more java docs to this class or the getter to describe what could be null in what scenarios.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would agree they could not unpack it. And just the fact that there is something encoded in any way gives a developer enough clue that there is a speciifc error and they should be able to unpack it sooner or later. It is better than hiding info completely. Another approach may be instead of storing any, convert it to string and store as a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @blakeli0 for your hard work. Java seems more complicated than other languages.
LGTM on actionable error logic. Please wait for the Java expertise review on language idiom or syntax.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good, but I'm concerned with the extra dependency in gax module. Gax is a very core dependency of all google cloud libraries, adding something there is a big deal. Looks like up untill this point, gax was not aware of protobuf, and maybe we should keep it this way (i.e. in ErrorDetails
do not have those error details classes directly. Maybe have some sort of GrpcErrorDetails in gax-grpc implementing a more generic ErrorDetails
from gax.
|
||
private void addErrorDetail(ErrorDetails.Builder errorDetailsBuilder, Any detail) { | ||
try { | ||
if (detail.is(ErrorInfo.class)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering, is it possible to convert this to a switch statement somehow? For example using class names as strings, as switch accepts strings as the switched value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave it some thoughts as well but couldn't find a good way to use switch statements, I don't think using class names as String would work either. Because we are not checking a variable, we are checking the result of method call, and the input of the method call is a variable. We can only get the type of Any
through this method call instead of an attribute, otherwise we could've check it easily.
Let me know if you have some other good ideas.
} catch (InvalidProtocolBufferException e) { | ||
// If unpacking one of the error detail fails, it should not block unpacking other error | ||
// details so that end users can still get some info back. Hence, we don't need to do | ||
// anything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we in his case just store the raw Any (not unpacked) detail
object in ErrorDetails
? Doing empyt error details will just loose information for users and make the error even more confusing to them.
|
||
public CancelledException( | ||
Throwable cause, StatusCode statusCode, boolean retryable, ErrorDetails errorDetails) { | ||
super(cause, statusCode, retryable, errorDetails); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep. It is a typical approach: to have some sort of a "master" consturctor and make other constructors simply call the big one. The master constructor can even be private, if we don't want to expose it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned, in my previous comment overall it looks good, but we need to figure out the dependency problem first and placement of the new classes. Putting "request changes" here to prevent accidental push of this PR before we figure out the deps part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but please address the remaining comments first.
Looks like I have to accept the new dependencies thing, but please check the comments (and i have a few questions there).
For the new methods in ApiExcepton - please strongly consider removing them. The cross-language desing usually never dictates such implementation-specific things. @summer-ji-eng please advaice.
} catch (InvalidProtocolBufferException e) { | ||
// If unpacking one of the error detail fails, it should not block unpacking other error | ||
// details so that end users can still get some info back. Hence, we don't need to do | ||
// anything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would agree they could not unpack it. And just the fact that there is something encoded in any way gives a developer enough clue that there is a speciifc error and they should be able to unpack it sooner or later. It is better than hiding info completely. Another approach may be instead of storing any, convert it to string and store as a string.
@@ -5,7 +5,8 @@ project.version = "2.12.3-SNAPSHOT" // {x-version-update:gax:current} | |||
|
|||
dependencies { | |||
api(libraries['maven.com_google_api_api_common'], | |||
libraries['maven.com_google_auth_google_auth_library_credentials'], | |||
libraries['maven.com_google_api_grpc_proto_google_common_protos'], | |||
libraries['maven.com_google_auth_google_auth_library_credentials'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this one needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean com_google_auth_google_auth_library_credentials
? I think It's a formatting/git issue, I added com_google_api_grpc_proto_google_common_protos
but somehow git thinks I removed one and added two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Just one minor comment on adding a message to ProtocolBufferParsingException
.
Kudos, SonarCloud Quality Gate passed! |
Kudos, SonarCloud Quality Gate passed! |
This PR is for issue #1635
Key design notes:
ApiException
created for GRPC clients will go throughGrpcApiExceptionFactory
and the cause ofApiException
already has all the info we need, hence the majority of the logic is inGrpcApiExceptionFactory
.ErrorInfo
to top level ofApiException
, accessible through getters directly.ErrorDetails
. The unpacked error messages are accessible through getters.ProtocolBufferParsingException
will be thrown.